
package com.upbos.sso.storage.impl;

import com.upbos.cache.SingleCacheManager;
import com.upbos.cache.props.CacheProps;
import com.upbos.core.util.StringPool;
import com.upbos.sso.entity.*;
import com.upbos.sso.exception.SsoException;
import com.upbos.sso.exception.SsoExceptionCode;
import com.upbos.sso.storage.StorageManager;
import com.upbos.sso.Constants;
import com.upbos.sso.entity.*;
import com.upbos.sso.storage.SessionKickOutListener;
import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
 * <b>Application name：</b> RedisCacheStorageManager.java <br>
 * <b>Application describing： </b> <br>
 * <b>Copyright：</b> Copyright &copy; 2019 upbos.com 版权所有。<br>
 * <b>Company：</b> upbos.com <br>
 * <b>@Date：</b> 2020年03月12日 17:11 <br>
 * <b>@author：</b> <a href="mailto:Jason@miyzh.com"> Jason </a> <br>
 * <b>@version：</b>V5.3.2 <br>
 */
public class CacheStorageManager implements StorageManager {


    private SingleCacheManager singleCacheManager;

    private SingleCacheManager localCacheManager;

    private SessionKickOutListener sessionKickOutListener;

    private Map<String, String> tokenCacheNames = new HashMap<String, String>();

    private Boolean singleSession = false;

    public void setSingleSession(Boolean singleSession) {
        this.singleSession = singleSession;
    }

    public void setTokenCacheName(String tokenCacheName) {
        this.tokenCacheNames.put(TokenType.WEB.getValue(), tokenCacheName);
    }

    public void setSessionKickOutListener(SessionKickOutListener listener) {
        this.sessionKickOutListener = listener;
    }

    public void setTokenCacheNames(Map<String, String> tokenCacheNames) {
        this.tokenCacheNames = tokenCacheNames;
    }

    public void setLocalCacheManager(SingleCacheManager localCacheManager) {
        this.localCacheManager = localCacheManager;
    }

    public void setSingleCacheManager(SingleCacheManager singleCacheManager) {
        this.singleCacheManager = singleCacheManager;
    }


    @Override
    public void saveToken(Token token) {
        if (token == null) {
            throw new SsoException(SsoExceptionCode.TOKEN_NULL);
        }

        TokenIdCache tokenIdCache = new TokenIdCache(token.getId());
        if (singleSession && sessionKickOutListener != null) {
            Token cacheToken = singleCacheManager.getCache(tokenIdCache.getCacheName()).getForHash(tokenIdCache.getCacheKey(), Constants.SESSION_CACHE_TOKEN_KEY_NAME);
            if (cacheToken != null) {
                sessionKickOutListener.notice(cacheToken);
            }
        }
        singleCacheManager.getCache(tokenIdCache.getCacheName()).putForHash(tokenIdCache.getCacheKey(), Constants.SESSION_CACHE_TOKEN_KEY_NAME, token);
    }

    @Override
    public void removeToken(String tokenId) {
        if (localCacheManager != null) {
            localCacheManager.getCache(CacheProps.DEFAULT_CACHE_NAME).remove(tokenId);
        }

        TokenIdCache tokenIdCache = new TokenIdCache(tokenId);
        singleCacheManager.getCache(tokenIdCache.getCacheName()).remove(tokenIdCache.getCacheKey());
    }

    @Override
    public Token getToken(String tokenId) {
        if (tokenId == null || "".equals(tokenId)) {
            return null;
        }

        // 解析会话在缓存里存放的缓存名称和key
        TokenIdCache tokenIdCache = new TokenIdCache(tokenId);
        if (tokenIdCache.getCacheName() == null) {
            return null;
        }
        // 先从本地缓存中获取token, 如果未找到，则从共享缓存中获取token
        Token token = localCacheManager != null ?
                localCacheManager.getCache(CacheProps.DEFAULT_CACHE_NAME).get(tokenId) : null;
        if (token == null) {
            token = singleCacheManager.getCache(tokenIdCache.getCacheName()).getForHash(tokenIdCache.getCacheKey(), Constants.SESSION_CACHE_TOKEN_KEY_NAME);
        }

        // 验证从缓存中取出来的token是不是当前会话
        if (token != null && !token.getId().equals(tokenId)) {
            return null;
        }

        // 更新本地缓存
        if (localCacheManager != null && token != null) {
            localCacheManager.getCache(CacheProps.DEFAULT_CACHE_NAME).set(tokenId, token);
        }

        return token;
    }

    @Override
    public boolean existToken(String tokenId) {
        return getToken(tokenId) == null ? false : true;
    }

    @Override
    public void saveSsoUser(String tokenId, SsoUser ssoUser) {
        TokenIdCache tokenIdCache = new TokenIdCache(tokenId);
        singleCacheManager.getCache(tokenIdCache.getCacheName()).putForHash(tokenIdCache.getCacheKey(), Constants.SESSION_CACHE_USER_KEY_NAME, ssoUser);
    }

    @Override
    public SsoUser getSsoUser(String tokenId) {
        TokenIdCache tokenIdCache = new TokenIdCache(tokenId);
        return singleCacheManager.getCache(tokenIdCache.getCacheName()).getForHash(tokenIdCache.getCacheKey(), Constants.SESSION_CACHE_USER_KEY_NAME);
    }

    @Override
    public void setAttr(String tokenId, String key, Object value) {
        TokenIdCache tokenIdCache = new TokenIdCache(tokenId);
        singleCacheManager.getCache(tokenIdCache.getCacheName()).putForHash(tokenIdCache.getCacheKey(), key, value);
    }

    @Override
    public void removeAttr(String tokenId, String key) {
        TokenIdCache tokenIdCache = new TokenIdCache(tokenId);
        singleCacheManager.getCache(tokenIdCache.getCacheName()).removeForHash(tokenIdCache.getCacheKey(), key);
    }

    @Override
    public <T> T getAttr(String tokenId, String key) {
        TokenIdCache tokenIdCache = new TokenIdCache(tokenId);
        return singleCacheManager.getCache(tokenIdCache.getCacheName()).getForHash(tokenIdCache.getCacheKey(), key);
    }

    @Override
    public String getStorageType() {
        return StorageType.CACHE.getValue();
    }

    @Data
    class TokenIdCache {
        private String cacheName;
        private String cacheKey;

        TokenIdCache(String tokenId) {
            TokenId tokenIdWrap = TokenId.parse(tokenId);
            if (tokenIdWrap == null) {
                return;
            }
            this.cacheName = tokenCacheNames.get(tokenIdWrap.getTokenType());
            if (this.cacheName == null) {
                this.cacheName = tokenIdWrap.getTokenType();
            }
            this.cacheKey = singleSession ?
                    tokenIdWrap.getUid() : tokenIdWrap.getUid() + StringPool.COLON + tokenIdWrap.getSessionId();
        }
    }
}
